#include "iotgo_mySocketClient_interface.h"
#include "iotgo_config.h"
#include "iotgo_platformAPI.h"
#include "iotgo_memory_hooks.h"
#include "iotgo_debug.h"

#if (POLARSSL_ON)
#include "polarssl/platform.h"
#include "polarssl/memory.h"

#define debugPrint socketClientDebugPrint

#define MAX_DOMAIN_LEN 20

/*macro: socket state*/
#define SOCKET_NOT_INITED         0
#define SOCKET_OBJ_IS_READY       1
#define SOCKET_FD_IS_READY        2
#define SOCKET_SSL_IS_READY       3
#define SOCKET_HANDSHAKE_IS_READY 4

struct mySocketClient_interface
{
    int32_t con_fd;
    volatile int8_t  state;
    uint8_t domain[MAX_DOMAIN_LEN];
    int32_t con_port;
    ssl_context ssl;
    fd_set readfds;
};


static int my_random(void *p_rng, uint8_t *output, size_t output_len);

MySocketClient *setupSocketClient(uint8_t *server_ip,uint32_t server_port)
{
    MySocketClient *p_socket = NULL;

    p_socket = (MySocketClient *) pMalloc(sizeof(MySocketClient));

    if(!p_socket)
    {
	debugPrint("[%s] p_socket for ssl is null\n",__FUNCTION__);
	return NULL;
    }

    debugPrint("[%s] p_socket for ssl is done\n",__FUNCTION__);

    memset(p_socket,0,sizeof(MySocketClient));
    p_socket->state = SOCKET_OBJ_IS_READY;
    strcpy(p_socket->domain,server_ip);
    p_socket->con_port = server_port;
    memory_set_own(pMalloc, pFree);
    return p_socket;
}


int8_t cleanSocketClient(MySocketClient **pp_mysocket)
{
	MySocketClient *p_mysocket = NULL;
	p_mysocket = *pp_mysocket;
    if(p_mysocket != NULL)
    {
        switch(p_mysocket->state)
        {
        case SOCKET_HANDSHAKE_IS_READY:
            debugPrint("[%s] close ssl connection\n",__FUNCTION__);
            ssl_close_notify(&(p_mysocket->ssl));
            p_mysocket->state--; 
        case SOCKET_SSL_IS_READY:
            debugPrint("[%s] free ssl object\n",__FUNCTION__);
            ssl_free(&(p_mysocket->ssl));
            p_mysocket->state--; 
        case SOCKET_FD_IS_READY:
            debugPrint("[%s] net close\n",__FUNCTION__);
            net_close(p_mysocket->con_fd);
            p_mysocket->state--; 
        case SOCKET_OBJ_IS_READY:
            debugPrint("[%s] free p_mysocket\n",__FUNCTION__);
            if(p_mysocket)
            {
                pFree(p_mysocket);
                debugPrint("[%s] clean done\n",__FUNCTION__);
                return MYSOCKETCLIENT_SUCCESS;
            }
            break;
        case SOCKET_NOT_INITED:
            return MYSOCKETCLIENT_SUCCESS;
        default:
            break;
        }
    }
    return MYSOCKETCLIENT_FAIL;
}


int8_t connectToServer(MySocketClient *p_mysocket)
{
    int8_t ret = -1;
    int32_t con_fd = -1;

	//vTaskDelay(10000);
	  
    switch(p_mysocket->state)
    {
    case SOCKET_OBJ_IS_READY:
	debugPrint("[%s] start net_connect...\n",__FUNCTION__);
	ret = net_connect(&(p_mysocket->con_fd),
			  p_mysocket->domain,
			  p_mysocket->con_port);

	//ret = net_connect(&con_fd,"54.223.98.144",443);
	debugPrint("[%s] net_connet ret is %d\n",__FUNCTION__,ret);
	if(ret != 0)
	{
	    debugPrint("[%s] make connect fd fail\n",__FUNCTION__);
	    debugPrint("[%s] server_ip is %s\n",__FUNCTION__,p_mysocket->domain);
	    debugPrint("[%s] server_ip is %d\n",__FUNCTION__,p_mysocket->con_port);
	    return MYSOCKETCLIENT_FAIL;
	}
	debugPrint("[%s] socket connect done\n",__FUNCTION__);
	p_mysocket->state = SOCKET_FD_IS_READY;
	ret = MYSOCKETCLIENT_NOT_FINISHED;
	break;
    case SOCKET_FD_IS_READY:
	memset(&(p_mysocket->ssl), 0, sizeof(ssl_context));
	if((ret = ssl_init(&(p_mysocket->ssl))) != 0) 
	{
	    debugPrint("[%s] ERROR: ssl_init failed !\n",__FUNCTION__);
	    debugPrint("[%s]        ssl_init return (%d)\n",__FUNCTION__,ret);
	    ssl_free(&(p_mysocket->ssl));
	    net_close(p_mysocket->con_fd);
	    p_mysocket->state = SOCKET_OBJ_IS_READY;
	    return MYSOCKETCLIENT_FAIL;
	}
	
	p_mysocket->state = SOCKET_SSL_IS_READY;
	ssl_set_endpoint(&(p_mysocket->ssl), SSL_IS_CLIENT);
	ssl_set_authmode(&(p_mysocket->ssl), SSL_VERIFY_NONE);
	ssl_set_rng(&(p_mysocket->ssl), my_random, NULL);
	ssl_set_bio(&(p_mysocket->ssl), 
		    net_recv, 
		    &(p_mysocket->con_fd), 
		    net_send, 
		    &(p_mysocket->con_fd));

	ret = MYSOCKETCLIENT_NOT_FINISHED;
	debugPrint("[%s] ssl init done\n",__FUNCTION__);
	break;
    case SOCKET_SSL_IS_READY:
	debugPrint("[%s] start handshake\n",__FUNCTION__);
	ret = ssl_handshake(&(p_mysocket->ssl));
	debugPrint("[%s] handshake return is %d\n",__FUNCTION__,ret);
	if(ret != 0)
	{
	    debugPrint("[%s] ERROR: ssl_handshake failed !\n",__FUNCTION__);
	    debugPrint("[%s]        ssl_handshake return (-0x%x)\n",__FUNCTION__,-ret);
	    ssl_free(&(p_mysocket->ssl));
	    net_close(p_mysocket->con_fd);
	    p_mysocket->state = SOCKET_OBJ_IS_READY;
	    return MYSOCKETCLIENT_FAIL;
	}
	debugPrint("[%s] ssl_handshake is done\n",__FUNCTION__);
	debugPrint("[%s] ssl_state is %d\n",__FUNCTION__,p_mysocket->ssl.state);
	p_mysocket->state = SOCKET_HANDSHAKE_IS_READY;
	return MYSOCKETCLIENT_SUCCESS;
    default:
      ret = MYSOCKETCLIENT_NOT_FINISHED;
	break;
    }
    return ret;
}

int32_t readFromServer(MySocketClient *p_mysocket,uint8_t *buf, uint32_t len)
{
    int32_t ret = -1;
    struct timeval timeout;
    timeout.tv_sec = 10000;
    timeout.tv_usec = 0;
    if(p_mysocket->state == SOCKET_HANDSHAKE_IS_READY)
    {
        FD_ZERO(&(p_mysocket->readfds));
        FD_SET(p_mysocket->con_fd,&(p_mysocket->readfds));
        ret = select(FD_SETSIZE, &(p_mysocket->readfds),NULL,NULL,&timeout);
        if(ret < 0)
        {
        
        }
        else if(ret == 0)
        {
        
        }
        else
        {
            ret = ssl_read(&(p_mysocket->ssl),buf,len);
            debugPrint("[%s] ssl_read ret is %d\n",__FUNCTION__,ret);
        }
    }
    return ret;
}


int32_t readFromServerNonBlock(MySocketClient *p_mysocket,uint8_t *buf, uint32_t len)
{
    int32_t ret = -1;
    if(p_mysocket->state == SOCKET_HANDSHAKE_IS_READY)
    {
        ret = ssl_get_bytes_avail(&(p_mysocket->ssl));
        if(ret > 0 && ret < len + 1)
        {
            ret = ssl_read(&(p_mysocket->ssl),buf,len);
        }
    }
    return ret;
}

int32_t writeToServer(MySocketClient *p_mysocket,uint8_t *buf, uint32_t len)
{
    int32_t ret = -1;
    if(p_mysocket->state == SOCKET_HANDSHAKE_IS_READY)
    {
	ret = ssl_write(&(p_mysocket->ssl),buf,len);
    }
    return ret;
}

static void get_random_bytes(void *buf, size_t len)
{
    uint32_t ranbuf;
    uint32_t *lp;
    int32_t i, count;
    //debugPrint("[%s] => get_random_bytes\n",__FUNCTION__);
    count = len / sizeof(uint32_t);
    lp = (uint32_t *) buf;

    for(i = 0; i < count; i ++)
    {	    
        lp[i] = os_get_random();
        len -= sizeof(uint32_t);
    }

    if(len > 0) 
    {
        ranbuf = os_get_random();
        memcpy(&lp[i], &ranbuf, len);
    }
	//debugPrint("[%s] <= get_random_bytes\n",__FUNCTION__);
}

static int my_random(void *p_rng, uint8_t *output, size_t output_len)
{
    get_random_bytes(output, output_len);
    return 0;
}

#endif
